package com.wu.serviceImpl.sys;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.wu.base.ApiResponse;
import com.wu.base.ResultUtils;
import com.wu.constants.Constants;
import com.wu.entity.sys.SResourceDo;
import com.wu.entity.sys.SRoleDo;
import com.wu.entity.sys.SRoleResourceDo;
import com.wu.entity.sys.SUserRoleDo;
import com.wu.enums.SystemEnum;
import com.wu.enums.field.FlagEnum;
import com.wu.mapper.SResourceMapper;
import com.wu.mapper.SRoleMapper;
import com.wu.mapper.SRoleResourceMapper;
import com.wu.mapper.SUserRoleMapper;
import com.wu.pojo.reqeust.sys.AddOrUpdateResourceRequest;
import com.wu.pojo.reqeust.sys.DeleteResourceRequest;
import com.wu.pojo.reqeust.sys.ListResourceRequest;
import com.wu.pojo.reqeust.sys.ResourceAllRequest;
import com.wu.pojo.response.sys.ResourceListResponse;
import com.wu.pojo.response.sys.ResourceTreeResponse;
import com.wu.redis.RedisService;
import com.wu.service.sys.SResourceService;
import com.wu.util.CodeUtils;
import com.wu.util.DozerUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 权限资源表(SResource)表服务实现类
 *
 * @since 2020-12-29 13:20:28
 */
@Slf4j
@Service("sResourceService")
public class SResourceServiceImpl extends ServiceImpl<SResourceMapper, SResourceDo> implements SResourceService {
    @Resource
    private SResourceMapper sResourceMapper;
    @Resource
    private SRoleMapper sRoleMapper;
    @Resource
    private SRoleResourceMapper sRoleResourceMapper;
    @Resource
    private RedisService redisService;
    @Resource
    private SUserRoleMapper sUserRoleMapper;

    @Override
    public ApiResponse listResource(ListResourceRequest request) {
        PageHelper.startPage(request.getCurrentPage(), request.getPageSize());
        List<SResourceDo> sResourceListByName = sResourceMapper.getSResourceListByName(request.getResourceName());

        List<ResourceListResponse> resourceListResponses = DozerUtil.mapList(sResourceListByName, ResourceListResponse.class);

        return ResultUtils.setOk(resourceListResponses, new PageInfo<>(sResourceListByName));
    }

    @Override
    public ApiResponse treeList(ResourceAllRequest request) {
        Object cache = redisService.get(Constants.USER_RESOURCES_TREE + request.getCurrentUserId());
        if (!Objects.isNull(cache)) {
            List<ResourceTreeResponse> roleMenuResponses = JSON.parseArray(cache.toString(), ResourceTreeResponse.class);
            return ResultUtils.setOk(roleMenuResponses);
        }
        List<SResourceDo> resourceAll = sResourceMapper.getSResourceByUserId(request.getCurrentUserId(), 0);
        List<SResourceDo> firstResource = resourceAll.stream().filter(sResourceDo -> sResourceDo.getParentCode().equals("0")).collect(Collectors.toList());
        List<ResourceTreeResponse> resourceTreeResponses = resourceSortTree(firstResource, resourceAll);
//        List<ResourceTreeResponse> resourceTreeResponses = getResourceTree(firstResource.stream().map(SResourceDo::getCode).collect(Collectors.toList()), 0);
        redisService.set(Constants.USER_RESOURCES_TREE + request.getCurrentUserId(), JSON.toJSONString(resourceTreeResponses), 10800);
        return ResultUtils.setOk(resourceTreeResponses);
    }

    @Override
    public ApiResponse treeListAll(ResourceAllRequest request) {
        Object cache = redisService.get(Constants.ALL_RESOURCES_TREE);
        if (!Objects.isNull(cache)) {
            List<ResourceTreeResponse> roleMenuResponses = JSON.parseArray(cache.toString(), ResourceTreeResponse.class);
            return ResultUtils.setOk(roleMenuResponses);
        }

        QueryWrapper<SResourceDo> queryAll = new QueryWrapper<>();
        queryAll.eq("flag", FlagEnum.NORMAL.getCode())
                .eq("is_ignore", 0)
                .groupBy("code");
        List<SResourceDo> resourceAll = sResourceMapper.selectList(queryAll);
        List<SResourceDo> firstResource = resourceAll.stream().filter(sResourceDo -> sResourceDo.getParentCode().equals("0")).collect(Collectors.toList());
        List<ResourceTreeResponse> resourceTreeResponses = resourceSortTree(firstResource, resourceAll);
        redisService.set(Constants.ALL_RESOURCES_TREE, JSON.toJSONString(resourceTreeResponses), 10800);
        return ResultUtils.setOk(resourceTreeResponses);
    }

    @Override
    public void sync(ResourceAllRequest request) {
        Object cache = redisService.get(Constants.ALL_RESOURCES_TREE);
        if (!Objects.isNull(cache)) {
            List<ResourceTreeResponse> roleMenuResponses = JSON.parseArray(cache.toString(), ResourceTreeResponse.class);
            String re = JSON.toJSONString(roleMenuResponses);
            log.info("BEFORE-RESOURCE:{}", re);
            redisService.set(Constants.ALL_RESOURCES_TREE + "-BACKUP", re, 10800);
        }
        QueryWrapper<SRoleDo> queryRole = new QueryWrapper<>();
        queryRole.eq("flag", FlagEnum.NORMAL.getCode())
                .eq("type", 1);
        SRoleDo roleDo = sRoleMapper.selectOne(queryRole);
        if (null == roleDo) {
            roleDo = new SRoleDo();
            roleDo.setType(1);
            roleDo.setName("管理员");
            sRoleMapper.insert(roleDo);
        }
        QueryWrapper<SResourceDo> queryAll = new QueryWrapper<>();
        queryAll.eq("flag", FlagEnum.NORMAL.getCode());
        List<SResourceDo> resourceAll = sResourceMapper.selectList(queryAll);
        for (SResourceDo resourceDo : resourceAll) {
            QueryWrapper<SRoleResourceDo> queryRoleResource = new QueryWrapper<>();
            queryRoleResource.eq("flag", FlagEnum.NORMAL.getCode())
                    .eq("role_id", roleDo.getId())
                    .eq("resource_id", resourceDo.getId())
                    .last("Limit 1");
            SRoleResourceDo roleResourceDo = sRoleResourceMapper.selectOne(queryRoleResource);
            if (null == roleResourceDo) {
                SRoleResourceDo roleResource = new SRoleResourceDo();
                roleResource.setResourceId(resourceDo.getId());
                roleResource.setRoleId(roleDo.getId());
                sRoleResourceMapper.insert(roleResource);
            }
        }

        QueryWrapper<SUserRoleDo> queryUserRole = new QueryWrapper<>();
        queryUserRole.eq("flag", FlagEnum.NORMAL.getCode())
                .eq("role_id", roleDo.getId());
        List<SUserRoleDo> sUserRoleDos = sUserRoleMapper.selectList(queryUserRole);
        for (SUserRoleDo sUserRoleDo : sUserRoleDos) {
            redisService.del(Constants.USER_RESOURCES + sUserRoleDo.getUserId(), Constants.SHIRO_CACHE + Constants.SHIRO_USER_RESOURCES + ":" + sUserRoleDo.getUserId());
        }
        redisService.del(Constants.ALL_RESOURCES_TREE);
    }

    @Override
    public ApiResponse addOrUpdateResource(AddOrUpdateResourceRequest request, Long userId) {
        String resourceName = request.getResourceName().trim();
        Long resourceId = request.getResourceId();
        //修改
        int count = sResourceMapper.judgeIsExistResourceName(resourceName, resourceId);
        if (count > 0) {
            return ResultUtils.setError(SystemEnum.RESOURCE_EXIST);
        }
        int i = 0;
        SResourceDo sResourceDo = new SResourceDo();
        sResourceDo.setName(resourceName);
        if (resourceId == null || resourceId == 0) {
            //新增
            sResourceDo.setCreater(userId);
            sResourceDo.setCreateTime(new Date());
            sResourceDo.setParentCode(request.getParentCode());
            sResourceDo.setCode(CodeUtils.getSystemCode());
            i = sResourceMapper.insert(sResourceDo);
        } else {
            sResourceDo.setId(resourceId);
            sResourceDo.setModifier(userId);
            sResourceDo.setModifyTime(new Date());
            i = sResourceMapper.updateById(sResourceDo);
        }
        if (i > 0) {
            return ResultUtils.setOk();
        }
        return ResultUtils.setError(SystemEnum.FAILED.getCode());
    }

    @Override
    public ApiResponse deleteResource(DeleteResourceRequest request, Long userId) {
        SResourceDo sr = sResourceMapper.selectById(
                new QueryWrapper<SResourceDo>().eq("id", request.getResourceId()).eq("flag", 0));
        if (sr == null) {
            return ResultUtils.setError(SystemEnum.RESOURCE_NO_EXIST);
        }
        // 删除之前首先判断资源是否被使用，如果已经被使用，不能删除
        int count = sRoleResourceMapper.countByResourceId(request.getResourceId());
        if (count > 0) {
            return ResultUtils.setError(SystemEnum.RESOURCE_BE_USED);
        }
        //判断是否存在关联子数据，存在，无法删除
        int countSon = sResourceMapper.countSonByParentCode(sr.getParentCode());
        if (countSon > 0) {
            return ResultUtils.setError(SystemEnum.RESOURCE_HAVE_SON);
        }
        //删除
        SResourceDo sResourceDo = new SResourceDo();
        sResourceDo.setId(request.getResourceId());
        sResourceDo.setModifyTime(new Date());
        sResourceDo.setModifier(userId);
        sResourceDo.setFlag(1);
        int i = sResourceMapper.updateById(sResourceDo);
        if (i > 0) {
            return ResultUtils.setOk();
        }
        return ResultUtils.setError(SystemEnum.FAILED.getCode());
    }

    @Override
    public Set<String> getResourceByUserId(Long userId) {
        String key = Constants.USER_RESOURCES + userId;
        boolean hasKey = redisService.hasKey(key);
        if (hasKey) {
            String s = redisService.get(key).toString();
            return new HashSet<>(JSON.parseArray(s, String.class));
        } else {
            List<SResourceDo> sResourceByUserId = sResourceMapper.getSResourceByUserId(userId, null);
            Set<String> collect = sResourceByUserId.stream().map(SResourceDo::getUrl).collect(Collectors.toSet());
            redisService.set(key, JSON.toJSONString(collect), 10800);
            return collect;
        }
    }

    private List<ResourceTreeResponse> getResourceTree(List<String> codes, Integer ignore) {
        List<ResourceTreeResponse> result = Lists.newArrayList();
        if (CollectionUtils.isEmpty(codes)) {
            return result;
        }

        for (String code : codes) {
            ResourceTreeResponse treeResponse = new ResourceTreeResponse();
            QueryWrapper<SResourceDo> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("flag", FlagEnum.NORMAL.getCode())
                    .eq("code", code)
                    .last("Limit 1");
            SResourceDo sDepartmentDo = sResourceMapper.selectOne(queryWrapper);
            if (null == sDepartmentDo) {
                continue;
            }
            treeResponse.setName(sDepartmentDo.getName());
            treeResponse.setId(sDepartmentDo.getId());
            treeResponse.setCode(sDepartmentDo.getCode());
            QueryWrapper<SResourceDo> querySub = new QueryWrapper<>();
            querySub.eq("flag", FlagEnum.NORMAL.getCode())
                    .eq("parent_code", code)
                    .eq(null != ignore, "is_ignore", ignore);
            List<SResourceDo> sDepartmentDos = sResourceMapper.selectList(querySub);
            treeResponse.setSubList(getResourceTree(sDepartmentDos.stream().map(SResourceDo::getCode).collect(Collectors.toList()), ignore));
            result.add(treeResponse);
        }
        return result;
    }

    private List<ResourceTreeResponse> resourceSortTree(List<SResourceDo> currents, List<SResourceDo> sourceDoList) {
        List<ResourceTreeResponse> result = Lists.newArrayList();
        List<SResourceDo> sourceList = new ArrayList<>(sourceDoList);

        if (CollectionUtils.isEmpty(currents)) {
            return result;
        }

        for (SResourceDo resourceDo : currents) {
            ResourceTreeResponse treeResponse = new ResourceTreeResponse();
            treeResponse.setCode(resourceDo.getCode());
            treeResponse.setId(resourceDo.getId());
            treeResponse.setName(resourceDo.getName());

            List<SResourceDo> subList = sourceDoList.stream().filter(resource -> resource.getParentCode().equals(resourceDo.getCode())).collect(Collectors.toList());
            sourceList.removeAll(subList);
            treeResponse.setSubList(resourceSortTree(subList, sourceList));
            result.add(treeResponse);
        }
        return result;
    }


}